Skip to content

consumer: sort fallback DML before flush - #5824

Merged
ti-chi-bot[bot] merged 3 commits into
pingcap:masterfrom
wk989898:consumer-0730
Jul 31, 2026
Merged

consumer: sort fallback DML before flush#5824
ti-chi-bot[bot] merged 3 commits into
pingcap:masterfrom
wk989898:consumer-0730

Conversation

@wk989898

@wk989898 wk989898 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

What problem does this PR solve?

Issue Number: close #5713

What is changed and how it works?

The consumer no longer drops fallback DML during append if the current message commit-ts is greater than global watermark. It keeps the DML in EventsGroup, then sorts and flushes it when a DDL or watermark arrives.

In the BAB case, if the consumer receives B first and later receives replayed A B, A is no longer ignored because its commitTs is smaller than the high watermark. It is buffered, then sorted into A B before writing.

Check List

Tests

  • Unit test
  • Integration test

Questions

Will it cause performance regression or break compatibility?
Do you need to update user documentation, design documentation or monitoring documentation?

Release note

Please refer to [Release Notes Language Style Guide](https://pingcap.github.io/tidb-dev-guide/contribute-to-tidb/release-notes-style-guide.html) to write a quality release note.

If you don't think this PR needs a release note then fill it with `None`.

Summary by CodeRabbit

Summary by CodeRabbit

  • Bug Fixes

    • Improved event processing to reliably order out-of-sequence data changes by commit timestamp before flushing.
    • Ensured messages are retained and processed consistently across Kafka, Pulsar, and storage consumers.
    • Improved cleanup of processed events to prevent stale data from remaining in memory.
  • Tests

    • Added coverage for out-of-order messages, stable ordering of equal timestamps, and complete event retrieval.

Signed-off-by: wk989898 <nhsmwk@gmail.com>
@ti-chi-bot

ti-chi-bot Bot commented Jul 30, 2026

Copy link
Copy Markdown

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@ti-chi-bot ti-chi-bot Bot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. release-note Denotes a PR that will be considered when it comes time to generate release notes. labels Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR removes the force parameter from EventsGroup.AppendMessage, adds out-of-order sorting to ResolveInto by CommitTs, and updates Kafka, Pulsar, and storage consumer writers to filter DML below the global watermark and append remaining messages unconditionally.

Changes

Watermark-ordered DML buffering

Layer / File(s) Summary
EventsGroup resolution and draining
cmd/util/event_group.go, cmd/util/event_group_test.go
EventsGroup.AppendMessage no longer takes a force parameter. It accepts unsorted messages. ResolveInto partitions messages by resolve threshold, detects out-of-order commit timestamps, and stably sorts resolved messages by CommitTs. GetAllMessages now calls ResolveInto and clears the internal buffer. Tests cover ordering, stability, and buffer cleanup.
Consumer writer integration
cmd/kafka-consumer/writer.go, cmd/pulsar-consumer/writer.go, cmd/storage-consumer/consumer.go, cmd/kafka-consumer/writer_test.go, cmd/pulsar-consumer/writer_test.go
Kafka and Pulsar writers drop DML messages below the global watermark before creating or updating an event group, then append remaining messages unconditionally. Prior fallback branches based on enableTableAcrossNodes, protocol type, and partition-table detection are removed. Storage consumer calls AppendMessage without the boolean argument. New tests verify watermark-based flushing order and fallback DML retention or rejection.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • pingcap/ticdc#5590: Introduces the AppendMessage method with the force parameter that this PR removes and refines.

Poem

I'm a rabbit with messages in line,
Sorting each carrot by commit-time.
Watermarks guide the stream along,
Stable and ordered, never wrong.
Hop, flush, and clear the queue—
The sink gets every bite in view!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address [#5713] by retaining fallback DML, sorting out-of-order messages, and flushing them at watermark or DDL boundaries.
Out of Scope Changes check ✅ Passed All changes relate to fallback DML buffering, ordering, flushing, and corresponding tests; no unrelated scope is evident.
Title check ✅ Passed The title clearly summarizes the main change: sorting fallback DML before flushing.
Description check ✅ Passed The description covers the issue, implementation, and tests, but the compatibility, documentation, and release note sections remain unanswered.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ti-chi-bot ti-chi-bot Bot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Jul 30, 2026
Signed-off-by: wk989898 <nhsmwk@gmail.com>
@wk989898
wk989898 marked this pull request as ready for review July 30, 2026 10:16
@ti-chi-bot ti-chi-bot Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 30, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/storage-consumer/consumer.go (1)

297-318: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Storage consumer still drops fallback rows — inconsistent with the new sort-before-flush policy.

The Kafka and Pulsar writers now append unconditionally and rely on ResolveInto to order, but here a row with commitTs < group.HighWatermark is silently discarded unless enableTableAcrossNodes. Given the group sorts before draining, the drop is no longer needed and causes exactly the sink inconsistency this PR targets.

♻️ Align with the other consumers
-	if commitTs >= group.HighWatermark {
-		group.AppendMessage(message)
-		log.Debug("DML event append to the group",
-			zap.Uint64("commitTs", commitTs), zap.Uint64("highWatermark", group.HighWatermark),
-			zap.String("schema", schema), zap.String("table", table), zap.Int64("tableID", tableID),
-			zap.Stringer("eventType", message.RowType))
-		return
-	}
-	if enableTableAcrossNodes {
-		log.Warn("DML events fallback, but enableTableAcrossNodes is true, still append it",
-			zap.Uint64("commitTs", commitTs), zap.Uint64("highWatermark", group.HighWatermark),
-			zap.String("schema", schema), zap.String("table", table), zap.Int64("tableID", tableID),
-			zap.Stringer("eventType", message.RowType))
-		group.AppendMessage(message)
-		return
-	}
-	log.Warn("dml event commit ts fallback, ignore",
-		zap.Uint64("commitTs", commitTs),
-		zap.Any("highWatermark", group.HighWatermark),
-		zap.String("schema", schema),
-		zap.String("table", table),
-	)
+	highWatermark := group.HighWatermark
+	group.AppendMessage(message)
+	if commitTs < highWatermark {
+		log.Warn("DML event commit ts fallback, append it and sort before flush",
+			zap.Uint64("commitTs", commitTs), zap.Uint64("highWatermark", highWatermark),
+			zap.String("schema", schema), zap.String("table", table), zap.Int64("tableID", tableID),
+			zap.Stringer("eventType", message.RowType))
+	}

Note enableTableAcrossNodes becomes unused in this function; drop the parameter or keep it only as a log field.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/storage-consumer/consumer.go` around lines 297 - 318, Update the storage
consumer’s DML handling around the group.HighWatermark checks to append every
message unconditionally, including commitTs values below the high watermark,
relying on the group’s sort-before-flush behavior. Remove the now-unnecessary
enableTableAcrossNodes branch and parameter from the relevant function, or
retain it only as a log field, and eliminate the fallback-drop path.
🧹 Nitpick comments (4)
cmd/kafka-consumer/writer.go (1)

619-643: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Unbounded Warn per fallback row.

During a broker disruption every re-consumed row hits one of these two Warn paths, which can flood the log at row rate. Consider rate-limiting (or aggregating per table/flush) and using typed fields (zap.Uint64 for partitionWatermark, zap.Stringer for protocol) instead of zap.Any.

As per coding guidelines, "Logs are operational signals; see docs/agents/logging.md before adding, removing, or rewriting logs."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/kafka-consumer/writer.go` around lines 619 - 643, Rate-limit or aggregate
the fallback-row warnings in the relevant writer flow so broker disruptions
cannot emit one Warn log per row, while preserving visibility per table or
flush. Update the affected structured fields to use zap.Uint64 for
partitionWatermark and zap.Stringer for protocol, following the logging
guidance; keep the existing fallback behavior unchanged.

Source: Coding guidelines

cmd/kafka-consumer/writer_test.go (1)

271-303: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Times(2) for three appended messages relies on same-commit-ts batching — assert it explicitly.

Three messages (20, 10, 20) all resolve at watermark 20, so the expectation only holds if the flush path coalesces equal-commit-ts messages into one DMLEvent. That implicit coupling makes the test fragile; consider asserting the row count per flushed event, and add a short scenario comment as the neighbouring tests do.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/kafka-consumer/writer_test.go` around lines 271 - 303, Strengthen
TestWriterWrite_sortsOutOfOrderDMLByWatermark by recording each flushed
DMLEvent’s row count, asserting the two events contain the expected counts for
the 10 and coalesced 20 commit timestamps, and add a brief comment explaining
that the two messages at commit timestamp 20 must be batched together.
cmd/util/event_group.go (1)

61-104: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Avoid the per-call resolved allocation by sorting in place.

The compaction already partitions in place: resolved elements could be written into a second index of the same backing array, or better, do a stable partition and sort only the resolved segment. As written, every ResolveInto call allocates a slice sized to the whole buffer (cap 1024+ per table/partition), even in the common fully-ordered case.

If you prefer the current shape, at least allocate lazily (resolved only after the first resolvable message is seen) so no-op calls don't allocate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/util/event_group.go` around lines 61 - 104, Update ResolveInto to avoid
eagerly allocating the resolved slice for every call. Prefer compacting
resolvable messages within g.messages’ existing backing array, then sort only
the resolved segment when outOfOrder is detected, while preserving
remaining-message order and cleanup behavior; if retaining the current
structure, create resolved lazily only after the first message at or below
resolve is encountered.
cmd/pulsar-consumer/writer_test.go (1)

271-298: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Same implicit same-commit-ts batching assumption as the Kafka test.

Three appended messages but Times(2); the expectation depends on the flush path coalescing equal commit-ts rows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/pulsar-consumer/writer_test.go` around lines 271 - 298, The test
TestWriterWrite_sortsOutOfOrderDMLByWatermark appends three DML messages but
expects the mock sink's AddDMLEvent to be called only twice, depending on an
implicit batching behavior where messages with the same commit-ts are coalesced
during the flush. Add a comment in the test to explicitly document that the
expectation of Times(2) relies on the writer's flush path coalescing the two
messages with commit-ts 20 into a single AddDMLEvent call, making the batching
assumption clear to future readers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/kafka-consumer/writer.go`:
- Around line 617-628: Define one consistent policy for fallback rows below the
partition watermark and apply it in the Kafka branch around
messageWithPartitionCheck and the Pulsar mirrored fallback branch: either
explicitly document and rely on idempotent sink application, or prevent
advancing the partition watermark until the fallback row is flushed. Update
cmd/kafka-consumer/writer.go lines 617-628 and cmd/pulsar-consumer/writer.go
lines 506-515 with the same policy, ensuring EventsGroup.ResolveInto cannot
leave these rows applied out of order.

---

Outside diff comments:
In `@cmd/storage-consumer/consumer.go`:
- Around line 297-318: Update the storage consumer’s DML handling around the
group.HighWatermark checks to append every message unconditionally, including
commitTs values below the high watermark, relying on the group’s
sort-before-flush behavior. Remove the now-unnecessary enableTableAcrossNodes
branch and parameter from the relevant function, or retain it only as a log
field, and eliminate the fallback-drop path.

---

Nitpick comments:
In `@cmd/kafka-consumer/writer_test.go`:
- Around line 271-303: Strengthen TestWriterWrite_sortsOutOfOrderDMLByWatermark
by recording each flushed DMLEvent’s row count, asserting the two events contain
the expected counts for the 10 and coalesced 20 commit timestamps, and add a
brief comment explaining that the two messages at commit timestamp 20 must be
batched together.

In `@cmd/kafka-consumer/writer.go`:
- Around line 619-643: Rate-limit or aggregate the fallback-row warnings in the
relevant writer flow so broker disruptions cannot emit one Warn log per row,
while preserving visibility per table or flush. Update the affected structured
fields to use zap.Uint64 for partitionWatermark and zap.Stringer for protocol,
following the logging guidance; keep the existing fallback behavior unchanged.

In `@cmd/pulsar-consumer/writer_test.go`:
- Around line 271-298: The test TestWriterWrite_sortsOutOfOrderDMLByWatermark
appends three DML messages but expects the mock sink's AddDMLEvent to be called
only twice, depending on an implicit batching behavior where messages with the
same commit-ts are coalesced during the flush. Add a comment in the test to
explicitly document that the expectation of Times(2) relies on the writer's
flush path coalescing the two messages with commit-ts 20 into a single
AddDMLEvent call, making the batching assumption clear to future readers.

In `@cmd/util/event_group.go`:
- Around line 61-104: Update ResolveInto to avoid eagerly allocating the
resolved slice for every call. Prefer compacting resolvable messages within
g.messages’ existing backing array, then sort only the resolved segment when
outOfOrder is detected, while preserving remaining-message order and cleanup
behavior; if retaining the current structure, create resolved lazily only after
the first message at or below resolve is encountered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7e35b46b-c5d5-487a-bcba-86dfdaa87076

📥 Commits

Reviewing files that changed from the base of the PR and between 84654e2 and 2e585b2.

📒 Files selected for processing (7)
  • cmd/kafka-consumer/writer.go
  • cmd/kafka-consumer/writer_test.go
  • cmd/pulsar-consumer/writer.go
  • cmd/pulsar-consumer/writer_test.go
  • cmd/storage-consumer/consumer.go
  • cmd/util/event_group.go
  • cmd/util/event_group_test.go

Comment thread cmd/kafka-consumer/writer.go
@wk989898

Copy link
Copy Markdown
Collaborator Author

/test all

Signed-off-by: wk989898 <nhsmwk@gmail.com>
@ti-chi-bot ti-chi-bot Bot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Jul 31, 2026
@wk989898

Copy link
Copy Markdown
Collaborator Author

/test all

@ti-chi-bot ti-chi-bot Bot added approved needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels Jul 31, 2026
@ti-chi-bot ti-chi-bot Bot added the lgtm label Jul 31, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: 3AceShowHand, asddongmen

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:
  • OWNERS [3AceShowHand,asddongmen]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot Bot removed the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label Jul 31, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown

[LGTM Timeline notifier]

Timeline:

  • 2026-07-31 09:03:57.27748394 +0000 UTC m=+2173223.313578996: ☑️ agreed by 3AceShowHand.
  • 2026-07-31 09:24:11.541957279 +0000 UTC m=+2174437.578052325: ☑️ agreed by asddongmen.

@ti-chi-bot
ti-chi-bot Bot merged commit af33cc1 into pingcap:master Jul 31, 2026
25 checks passed
@wk989898 wk989898 added the needs-cherry-pick-release-8.5 Should cherry pick this PR to release-8.5 branch. label Jul 31, 2026
@ti-chi-bot

Copy link
Copy Markdown
Member

In response to a cherrypick label: new pull request created to branch release-8.5: #5833.
But this PR has conflicts, please resolve them!

@wk989898

Copy link
Copy Markdown
Collaborator Author

/cherry-pick release-nextgen-202603

@ti-chi-bot

Copy link
Copy Markdown
Member

@wk989898: new pull request created to branch release-nextgen-202603: #5834.
But this PR has conflicts, please resolve them!

Details

In response to this:

/cherry-pick release-nextgen-202603

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the ti-community-infra/tichi repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved lgtm needs-cherry-pick-release-8.5 Should cherry pick this PR to release-8.5 branch. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pulsar loss case stays inconsistent after checkpoint advances, suspected consumer-side issue

4 participants